package fr.loof.logeek;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;

/**
 * Instrument classes with logging based on SLF4J API
 * 
 * @author <a href="mailto:nicolas@apache.org">Nicolas De Loof</a>
 */
public class Slf4jLogging
    extends AbstractLoggingCodeTransformer
{
    protected void insertLogger( CtClass cl )
        throws Exception
    {
        cp.importPackage( "org.slf4j" );
        CtField logger =
            CtField.make( "private static transient Logger __logger " + " = LoggerFactory.getLogger( " + cl.getName()
                + ".class );", cl );
        cl.addField( logger );
    }

    @Override
    protected void log( StringBuffer sb, String level, String data )
    {
        sb.append( "__logger." + level + "(" + data + ");" );
    }

    protected void insertInlogging( CtMethod method )
        throws Exception
    {
        StringBuffer before = new StringBuffer();
        logMethodIn( before, "debug" );
        logMethodParameters( method, before, "trace" );
        method.insertBefore( before.toString() );
    }

    protected void insertExceptionLogging( CtMethod method )
        throws Exception
    {
        StringBuffer sb = new StringBuffer();
        logExceptionThrown( sb, "debug" );
        logMethodOut( sb, "debug" );
        sb.append( "throw $e;" );
        method.addCatch( sb.toString(), cp.get( "java.lang.Throwable" ) );
    }

    protected void insertOutLogging( CtMethod method )
        throws Exception
    {
        StringBuffer after = new StringBuffer();
        logReturnedValue( method, after, "trace" );
        logMethodOut( after, "debug" );
        method.insertAfter( after.toString() );
    }

    protected void logMethodIn( StringBuffer before, String level )
    {
        before.append( "__logger." + level + "( \"{\" );" );
    }

    protected void logMethodOut( StringBuffer sb, String level )
    {
        sb.append( "__logger." + level + "( \"}\" );" );
    }

    protected void logMethodParameters( CtMethod method, StringBuffer sb, String level )
        throws Exception
    {
        CtClass[] parameterTypes = method.getParameterTypes();
        for ( int i = 0; i < parameterTypes.length; i++ )
        {
            String name = getMethodParameterName( method, i );
            if ( parameterTypes[i].isPrimitive() )
            {
                sb.append( "__logger." + level + "( \"  " + name + " = {}\", ($w) $" + ( i + 1 ) + " );" );
            }
            else
            {
                sb.append( "__logger." + level + "( \"  " + name + " = {}\", $" + ( i + 1 ) + " );" );
            }
        }
    }

    protected void logReturnedValue( CtMethod method, StringBuffer sb, String level )
        throws Exception
    {
        CtClass returnType = method.getReturnType();
        if ( !returnType.equals( cp.get( "java.lang.Void" ) ) )
        {
            if ( returnType.isPrimitive() )
            {
                sb.append( "__logger." + level + "( \"  return {}\", ($w) $_ );" );
            }
            else
            {
                sb.append( "__logger." + level + "( \"  return {}\", $_ );" );
            }
        }
    }

    protected void logExceptionThrown( StringBuffer sb, String level )
    {
        sb.append( "__logger." + level + "( \"  throws {}\", $e );" );
    }
}
